package com.seed.application.context.impl;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.datasource.FlexDataSource;
import com.seed.application.Sa;
import com.seed.application.context.Custom;
import com.seed.application.context.SeedContext;
import com.seed.application.context.SeedWatcher;
import com.seed.application.env.AppEnv;
import com.seed.application.env.MicroEnv;
import com.seed.core.action.Action;
import com.seed.core.configuration.MicroConfiguration;
import com.seed.core.pojo.seed.Seed;
import com.seed.core.query.App;
import com.seed.core.query.Micro;
import com.seed.manager.LibManager;
import com.seed.uaa.Uaa;
import com.seed.uaa.util.MergeUtil;
import com.seed.util.ActionsKit;
import com.seed.util.AppLoadKit;
import com.seed.util.StrKit;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 开发的所有数据库都在一台服务器上
 */
@Slf4j
public class DevSeedContext implements SeedContext {

    private final App app;
    private final Map<String, App> apps = new ConcurrentHashMap<>();

    public DevSeedContext() {
        this.app = new App(Sa.getConfiguration());

        init(this.app);

        Thread daemonThread = new Thread(this::watch);
        daemonThread.setDaemon(true); // 将此线程标记为守护线程
        daemonThread.start(); // 启动守护线程
    }

    private void watch() {
        try {
            System.out.printf("开始监控目录【%s】\n", Sa.getPath().getAbsolutePath());
            new SeedWatcher(this).start();
        } catch (IOException e) {
            System.err.println("开启监控失败:" + e.getMessage());
        }
    }

    public void init(App app) {
        String owner = app.getOwner();
        String appName = app.getName();
        List<String> libNames = app.getConfigurationLibs();
        Map<String, Seed> libSeeds = LibManager.getLibSeeds(libNames);
        libSeeds.putAll(Uaa.getSeeds());
        Map<String, Seed> seeds = MergeUtil.initSeeds(app.getConfigurationSeeds(), libSeeds, Custom.getSeeds());

        Map<String, Action> libActions = LibManager.getLibActions(libNames);
        libActions.putAll(Uaa.getActions());
        Map<String, Action> actions = MergeUtil.initActions(
                ActionsKit.initMethods(app.getConfigurationActions()),
                libActions,
                Custom.getActions()
        );

        app.setSeeds(seeds);
        app.setActions(actions);
        String dataSourceKey = StrKit.dataSourceKey(owner, appName, null, null);
        run(dataSourceKey, seeds);
        // TODO 初始化租户库 切换到主库，然后查询租户信息

        Map<String, MicroConfiguration> microConfigurations = app.getConfigurationMicros();
        if(microConfigurations == null) return;

        Map<String, Micro> micros = new HashMap<>();
        for(Map.Entry<String, MicroConfiguration> entry: microConfigurations.entrySet()) {
            String microName = entry.getKey();
            List<String> microLibNames = app.getConfigurationLibs(microName);

            Map<String, Seed> microLibSeeds = LibManager.getLibSeeds(microLibNames);
            Map<String, Seed> microSeeds = MergeUtil.initSeeds(
                    app.getConfigurationSeeds(microName),
                    microLibSeeds,
                    Custom.getSeeds(microName)
            );

            Map<String, Action> microLibActions = LibManager.getLibActions(microLibNames);
            Map<String, Action> microActions = MergeUtil.initActions(
                    ActionsKit.initMethods(app.getConfigurationActions(microName)),
                    microLibActions,
                    Custom.getActions(microName)
            );

            micros.put(microName, new Micro(microSeeds, microActions));
            String microDataSourceKey = StrKit.dataSourceKey(owner, appName, null, microName);
            run(microDataSourceKey, microSeeds);
            // TODO 初始化租户库 切换到主库，然后查询租户信息
        }
        app.setMicros(micros);
    }

    private void run(String dataSourceKey, Map<String, Seed> seeds) {
        HikariDataSource dataSource = getHikariDataSource(dataSourceKey);
        FlexDataSource flexDataSource = FlexGlobalConfig.getDefaultConfig().getDataSource();
        flexDataSource.addDataSource(dataSourceKey, dataSource);
        log.info("开始创建【{}】数据库及表,以及修改表结构", dataSourceKey);
        DataSourceKey.use(dataSourceKey, () -> Sa.getDdl().run(seeds));
    }

    private HikariDataSource getHikariDataSource(String databaseName) {
        HikariDataSource ret = new HikariDataSource();
        Environment environment = Sa.getEnvironment();
        String url = environment.getProperty("spring.datasource.url");
        String username = environment.getProperty("spring.datasource.username");
        String password = environment.getProperty("spring.datasource.password");
        String params = "?serverTimezone=UTC&createDatabaseIfNotExist=true";
        String jdbcUrl = StrKit.cutInTwo(url, '?', false)[0];
        jdbcUrl = jdbcUrl.substring(0, jdbcUrl.lastIndexOf('/') + 1) + databaseName + params;
        ret.setJdbcUrl(jdbcUrl);
        ret.setUsername(username);
        ret.setPassword(password);

        return ret;
    }

    @Override
    public App getApp() {
        String appKey = getAppKey();
        if(StrUtil.isBlank(appKey)) return app;

        App app = apps.get(appKey);
        if(app == null) {
            app = initApp(getAppFile());
            apps.put(appKey, app);
        }

        return app;
    }

    @Override
    public Micro getMicro() {
        return getApp().getMicro(MicroEnv.getMicroName());
    }

    public App getApp(String owner, String appName) {
        String appKey = StrKit.appKey(owner, appName);
        return apps.get(appKey);
    }

    public String getAppKey() {
        String owner = AppEnv.getOwner();
        String appName = AppEnv.getAppName();
        if(StrUtil.isBlank(appName)) return null;

        return (StrUtil.isBlank(owner) ? "" : owner + '.') + appName;
    }

    private App initApp(File appFile) {
        App app = new App(AppLoadKit.load(appFile));
        init(app);

        return app;
    }

    public File getAppFile() {
        String owner = AppEnv.getOwner();
        String appName = AppEnv.getAppName();
        return getAppFile(owner, appName);
    }

    public File getAppFile(String owner, String appName) {
        if(StrUtil.isBlank(appName)) throw new RuntimeException("应用名称不能为空");

        String name = (StrUtil.isBlank(owner) ? "" : owner + File.separator) + appName;
        String fileMainName = Sa.getPath().getAbsolutePath() + File.separator + name;
        File file = new File(fileMainName + ".json");
        if(file.exists()) return file;

        file = new File(fileMainName + ".yml");
        if(file.exists()) return file;

        file = new File(fileMainName + ".yaml");
        if(file.exists()) return file;

        throw new RuntimeException(String.format("应用配置文件【%s】不存在", fileMainName));
    }

    public void reloadApp(String owner, String appName) {
        App newApp = initApp(getAppFile(owner, appName));
        String appKey = StrKit.appKey(owner, appName);
        apps.put(appKey, newApp);
    }

    public File getPath() {
        return Sa.getPath();
    }
}
